[Previous] [Next]

Overview of Native Data Types

Visual Basic for Applications supports several native data types, including integer and floating point numbers, strings, date and time values, and so on. You can store data in a variable of the proper type, or you can use the Variant data type—the default type in VBA—which is a sort of jolly data type that's able to host any type of data.

The Integer Data Type

Integer variables can hold integer values (whole numbers) included in the range from -32,768 through 32,767. These variables are also known as 16-bit integers because each value of this type takes 2 bytes of memory.

Integer variables were probably the most used type of variables, at least until Visual Basic made its debut on 32-bit Microsoft Windows platforms. For all practical purposes, in a 32-bit environment you can use a Long value instead of an Integer value without a performance hit, at the same time reducing the probability of an overflow error when the value of your variable outgrows the valid range of Integers. One of the few occasions when Integers should be preferred to Longs is when you create very large arrays. In all other cases, I suggest you use Long values, unless you have good reasons not to do so (as when you're calling an external program or a DLL that expects an Integer).

NOTE
You can indirectly specify that an undeclared variable is of type Integer by appending a % symbol to its name. However, this feature is supported by Visual Basic 6 only for compatibility with older Visual Basic and QuickBasic programs. All new applications should exclusively use variables declared in an explicit way. The same suggestion of course applies to other data types, including Long (&), Single(!), Double(#), Currency(@), and String($).

All the integer constants in your code are implicitly of type Integer, unless their value is outside the range for this data type, in which case they are stored as Long.

The Long Data Type

Long variables can hold integer values in the range from -2,147,483,648 through 2,147,483,647 and are also known as 32-bit integers because each value takes 4 bytes of memory. As I mentioned previously, you're encouraged to use Longs in your applications as the preferred data type for integer values. Long variables are as fast as Integer variables, and in most cases they prevent the program from breaking when dealing with numbers larger than expected. One example is when you have to process strings longer than 32,767 characters: In this case, you must use a Long index instead of an Integer variable. Watch out for this quirk when you convert code written for older Visual Basic versions.

As I explained previously, you are strongly advised not to declare Long variables with a trailing & character in their names. However, it's common practice to append an & symbol to those constants that would be stored as Integer but that you want the compiler to explicitly interpret as Long. Sometimes the difference can be important:

Result = value And &HFFFF     ' here &HFFFF means -1 
Result = value And &HFFFF&    ' here &HFFFF& means 65535

If you don't want to concentrate on such microscopic details, just declare an explicit constant:

Const LOWWORD_MASK As Long = &HFFFF&

CAUTION
For historical reasons, Visual Basic lets you enforce a particular data type as the default data type using the Deftype directive, so you might be tempted to use the DefLng A-Z directive at the beginning of each module to ensure that all undeclared variables are Long. My advice is: don't do that! Using Deftype directives instead of carefully declaring all your variables is a dangerous practice. Moreover, Deftype directives impair code reusability in that you can't safely cut and paste code from one module to another without also copying the directive.

The Boolean Data Type

Boolean variables are nothing but Integers that can hold only values 0 and -1, which stand for False and True, respectively. When you use a Boolean, you are actually wasting 15 out of 16 bits in the variable, because this information could be easily held in one single bit. That said, I suggest you use Boolean instead of Integer variables whenever it makes sense to do so because this increases the readability of your code. On a few occasions, I have also experienced a slight improvement in performance, but usually it's negligible and shouldn't be a decisive factor.

The Byte Data Type

Byte variables can hold an integer numeric value in the range 0 through 255. They take only one byte (8 bits) each and are therefore the smallest data type allowed by Visual Basic. Visual Basic 4 introduced the Byte data type to ease the porting of 16-bit applications to Windows 95 and Microsoft Windows NT. Specifically, while Visual Basic 4 for the 32-bit platform and later versions are source-code compatible with Visual Basic 3 and Visual Basic 4 for the 16-bit platform applications, they store their strings in Unicode instead of ANSI format. This difference raised a problem with strings passed to API functions because Visual Basic 3 programmers used to store binary data in strings for passing it to the operating system, but the Unicode-to-ANSI automatic conversion performed by Visual Basic makes it impossible to port this code to 32-bit without any significant change.

To make a long story short, the Byte data type was added to Visual Basic primarily to solve this problem. Apart from this advanced use, you should use Byte values only when you're dealing with arrays holding binary data. For individual values, an Integer or a Long variable is usually a better choice.

The Single Data Type

Single variables can hold decimal values in the range from -3.402823E38 through -1.401298E-45 for negative values and 1.401298E-45 through 3.402823E38 for positive values. They take 4 bytes and are the simplest (and least precise) of the floating point data types allowed by Visual Basic.

Contrary to what many programmers believe, Single variables aren't faster than Double variables, at least on the majority of Windows machines. The reason is that on most systems, all floating point operations are performed by the math coprocessor, and the time spent doing the calculations is independent of the original format of the number. This means that in most cases you should go with Double values because they offer a better precision, a wider range, fewer overflow problems, and no performance hit.

The Single data type is a good choice when you're dealing with large arrays of floating point values, and you can be satisfied with its precision and valid range. Another good occasion to use the Single data type is when you're doing intensive graphical work on your forms and in PictureBox controls. In fact, all the properties and methods that deal with coordinates—including CurrentX/Y, Line, Circle, ScaleWidth, ScaleHeight, and so on—use values of type Single. So you might save Visual Basic some conversion work if you store your coordinate pairs in Single variables.

The Double Data Type

Double variables can hold a floating point value in the range -1.79769313486232E308 through -4.94065645841247E-324 for negative values and 4.9406564581247E-324 through 1.79769313486232E308 for positive values. They take 8 bytes and in most cases are the preferable choice when you're dealing with decimal values. A few built-in Visual Basic functions return Double values. For example, the Val function always returns a Double value, even if the string argument doesn't include a decimal point. For this reason, you might want to store the result from such functions in a Double variable, which saves Visual Basic an additional conversion at run time.

The String Data Type

All 32-bit flavors of Visual Basic—Visual Basic 4 for 32-bit platforms, 5, and 6—store strings of characters in Unicode format, while all previous versions used the ANSI format. The difference is that Unicode uses two bytes for each character, so theoretically a Unicode character can assume as many as 65,536 different values. This makes Unicode strings ideal for writing code that displays its messages in non-Latin alphabets, such as Chinese, Japanese, and Hebraic. If you don't localize your software in these alphabets, you'll probably look at Unicode strings mainly as a way to waste memory in your program, especially if you use many long strings. Note that Windows NT and some portions of Windows 95/98 use Unicode strings.

Visual Basic manages two different types of strings: conventional variable-length strings and fixed-length strings. You declare them in different ways:

Dim VarLenStr As String
Dim FixedLenStr As String * 40

The first, obvious difference is that in any given moment a variable-length string takes only the memory that it needs for its characters (actually, it takes 10 additional bytes for holding other information about the string, including its length), whereas a fixed-length string always takes a fixed amount of memory (80 bytes, in the preceding example).

If you are a performance-savvy programmer, you should remember that conventional strings are usually faster than fixed-length string. This happens because all VBA native string functions can deal only with variable-length strings. In a sense, a fixed-length string is something that VBA isn't even aware of: When you pass a fixed-length string to a VBA function, the compiler generates hidden statements that convert that argument into a temporary variable-length string.

But even with all this overhead, fixed-length strings aren't always going to make your programs slower. For one, Visual Basic excels at allocating and releasing memory for fixed-length strings, so if your program spends a lot of time assigning new values to variables or creates large string arrays, fixed-length strings might prove even faster than conventional ones. Just to give you an example, on a 233-KHz system Visual Basic 6 takes about 9 seconds to load 100,000 30-character strings into a conventional string array, and 0.4 seconds to remove them. Both operations are completed almost instantaneously if performed on an array of fixed-length strings.

String constants are enclosed within quotes, and you can embed quotes within the string by doubling them:

Print "<My Name Is ""Tarzan"">"     ' displays  <My Name Is "Tarzan">

Visual Basic additionally defines a number of intrinsic string constants, such as vbTab (the Tab character) or vbCrLf (the carriage return-line feed pair). Using these constants usually improves the readability of your code as well as its performance because you don't have to use a Chr function to create the strings.

The Currency Data Type

Currency variables can hold decimal values in a fixed-point format, in the range from -922,337,203,685,477.5808 through 922,337,203,685,477.5807. They differ from floating-point variables, such as Single and Double, in that they always include four decimal digits. You can think of a currency value as a big integer that's 8 bytes long and whose value is automatically scaled by a factor of 10,000 when it's assigned to the variable and when it's read back and displayed to the user.

Using a fixed-point value has its advantages over floating-point variables. For one, Currency values suffer less from the rounding problems that you often experience using Double values. When you're adding or subtracting values, however, Currency variables don't offer a performance advantage, and multiplying and dividing Currency values is about five times slower than doing the same for Double values. Keep this in mind if your application does a lot of math.

The Date Data Type

Date variables can hold any date between January 1, 100, through December 31, 9999, as well as any time value. They take 8 bytes, exactly like Double variables. This isn't a casual resemblance because internally these date/time values are stored as floating-point numbers, in which the integer part stores the date information and the decimal part stores the time information. (For example, 0.5 means 12 A.M., 0.75 means 6 P.M., and so on.) Once you know how Date variables store their values, you can perform many meaningful math operations on them. For example, you can truncate date or time information using the Int function, as follows:

MyVar = Now                    ' MyVar is a Date variable.
DateVar = Int(MyVar)           ' Extract date information.
TimeVar = MyVar - Int(MyVar)   ' Extract time information.

You can also add and subtract dates, as you would do with numbers:

MyVar = MyVar + 7              ' Advance one week.
MyVar = MyVar - 365            ' Go back one (nonleap) year.

VBA provides many functions for dealing with date and time information in more advanced ways, which I'll cover in Chapter 5. You can also define a Date constant using the format #mm/dd/yyyy#, with or without a time portion:

MyVar = #9/5/1996 12.20 am#

The Object Data Type

Visual Basic uses object variables to store reference objects. Note that here we are talking about storing a reference to an object, not storing an object. The difference is subtle but important, and I'll talk about it at length in Chapter 6. There are several types of object variables, but they can be grouped in two broad categories: generic object variables and specific object variables. Here are a few examples:

' Examples of generic object variables
Dim frm As Form            ' A reference to any form
Dim midfrm As MDIForm      ' A reference to any MDI form
Dim ctrl As Control        ' A reference to any control
Dim obj As Object          ' A reference to any object
' Examples of specific object variables
Dim inv As frmInvoice      ' A reference to a specific type of form
Dim txtSalary As TextBox   ' A reference to a specific type of control
Dim cust As CCustomer      ' A reference to an object defined by a
                           ' class module in the current project
Dim wrk As Excel.Worksheet ' A reference to an external object

The most evident difference when dealing with object variables (as opposed to regular variables) is that you assign object references to them using the Set keyword, as in the following code:

Set frm = Form1
Set txtSalary = Text1

After the assignment, you can use the object variable to access the original object's properties and methods:

frm.Caption = "Welcome to Visual Basic 6"
txtSalary.Text = Format(99000, "currency")

CAUTION
One of the most common errors that programmers make when dealing with object variables is omitting the Set command during assignments. What happens if you omit this keyword depends on the object involved. If it doesn't support a default property, Visual Basic raises a compile-time error ("Invalid use of property"); otherwise, the assignment succeeds, but the result won't be the one you expect:

frm = Form1           ' A missing Set raises a compiler error.
txtSalary = Text1     ' A missing Set assigns Text1's Text property
                      ' to txtSalary's Text property.

Object variables can also be cleared so that they don't point to any particular object anymore. You do this by assigning them the special Nothing value:

Set txtSalary = Nothing

The Variant Data Type

Variant variables were introduced in Visual Basic 3, but their internal format changed in version 4, where their capabilities were greatly enhanced. The Variant format is defined by OLE, and so it's highly unlikely that it will be modified again in the future. Variant variables can hold any type of data described so far, and then some. Variables of this type take 16 bytes, in this format:

Click to view at full size.

Bytes 0 and 1 hold an integer value that states which type of data is stored in bytes 8 through 15. Bytes 2 through 7 are unused (with only one exception, the Decimal subtype), and in most cases not all the bytes in the second half of the variable are used. For example, if a Variant holds an Integer value, the first two bytes contain the value 2-vbInteger, bytes 8 and 9 hold the actual 16-bit value, and all other bytes are unused.

A Variant variable holds a value in its original format and doesn't enforce a metaformat that encompasses all the data types supported by Visual Basic. For example, when Visual Basic adds numbers held in two Variant variables, it checks their type and uses the most efficient math routine possible. And so, if you're adding two Variants that hold one Integer and one Long, Visual Basic promotes the Integer to Long and then invokes the routine for addition between Longs.

CAUTION
Automatic data coercion is always dangerous because you might not get the results that you expect. For example, if you use the + operator on two Variants that hold numeric values, Visual Basic interprets the + as the addition operator. If both values are strings, Visual Basic interprets the + as the append operator. When one data type is a string and the other is a number, Visual Basic tries to convert the string to a number so that an addition can be performed; if this isn't possible, a "Type Mismatch" error is raised. If you want to be sure to execute an append operation regardless of the data types involved, use the & operator. Finally note that you can't store fixed-length strings in Variant variables.

Variant is the default data type for Visual Basic. In other words, if you use a variable without declaring its type, as in the following line of code:

 Dim MyVariable

this will be a Variant variable, unless this line is preceded by a Deftype directive that sets a different default data type. Likewise, if you use a variable without first declaring it (and you don't use a Deftype directive), Visual Basic creates a Variant variable.

NOTE
If I could give only one suggestion to novice Visual Basic programmers, it would be this: Always add an Option Explicit directive at the beginning of every module in your programs. Even better, enable the Require Variable Declaration option in the General tab of the Options dialog box from the Tools menu so that Visual Basic automatically adds this directive whenever you create a new module. I can't overestimate the importance of having Visual Basic check for you that you haven't accidentally misspelled a variable's name. Be aware that some template projects create modules that lack the Option Explicit directive.

The type of data actually stored in a Variant variable depends on the last assignment to it. You can test the type of the current contents of such a Variable using the VarType function:

Dim v As Variant
v = True
Print VarType(v)     ' Prints "11", that is vbBoolean

Variant variables can also host special values that don't correspond to any data values described so far. The Empty value is the state of a Variant variable when nothing has been assigned to it yet. You can test this special value using the IsEmpty function, or you can test the VarType function for the value 0-vbEmpty:

Dim v As Variant
Print IsEmpty(v)     ' Prints "True" (uninitialized variant).
v = "any value"      ' The variant isn't empty anymore.
v = Empty            ' Restore the Empty state using the Empty constant.

The Null value is useful in database programming to mark fields that don't contain a value. You can explicitly assign the Null value to a Variant using the Null constant, test for a Null value using the IsNull function, or compare the return value of the VarType function with the value 1-vbNull:

v = Null             ' Stores a Null value
Print IsNull(v)      ' Prints "True"

Variant variables can also contain an Error value. This is useful, for example, if you want a routine to return a meaningful value if it succeeds or an error value if it doesn't. In this case, you declare a function that returns a Variant value: if no error occurs, you return the result. Otherwise, you use the CVErr function to create a Variant of subtype Error:

Function Reciprocal(n As Double) As Variant
    If n <> 0 Then
        Reciprocal = 1 / n
    Else
        Reciprocal = CVErr(11)    ' Division By Zero error code
    End If
End Function

You can test the Error subtype using the IsError function or by comparing the return value of VarType with the value 10-vbError. Error codes must be in the range 0 through 65535. To convert the error code into an integer, you can use the CLng function. Here's the typical client code for a function that could return an error code in a Variant:

Dim res As Variant
res = Reciprocal(CDbl(Text1.Text))
If IsError(res) Then
    MsgBox "Error #" & CLng(res)
Else
    MsgBox "Result is " & res
End If

I'm reporting this style of error trapping exclusively for the sake of completeness. My advice, in fact, is that you should never use this approach for error management; rather, you should rely on the Err object, which is able to convey more information about errors.

Variant variables can also host object values. You must assign object values using the Set keyword; otherwise, the results are unpredictable, as this short code snippet demonstrates:

Dim v As Variant
Set v = Text1      ' A correct object assignment that uses Set
v.Text = "abcde"   ' This works, because V points to Text1.
v = Text1          ' Wrong object assignment, Set is omitted.
                   ' Actually, it assigns the value of default property
                   ' and is equivalent to v = Text1.Text
Print v            ' Displays "abcde"
v.Text = "12345"   ' Error 424: Object Required

You can test whether a Variant holds an object using the IsObject function. Don't use VarType to test whether a Variant variable holds an object reference. In fact, if the object supports a default property, the VarType function returns the type of that property, not the vbObject constant.

Starting with Visual Basic 6, Variant variables can also hold user-defined type (UDT) structures, and the VarType function can return the new value 36vbUserDefinedType. But this capability is available only if the Type statement that defines the UDT structure appears with the Public scope attribute in a Public class module. You can't assign UDT structures to Variant variables within Standard EXE projects because they can't expose Public class modules.

You can use other functions to test the type of the value stored in a Variant variable. The IsNumeric function returns True if the value can be successfully converted to a number using the CDbl function, even if the native format is different. (The Variant variable holds a string, for example.) The IsDate function checks whether the value can be successfully converted to a date using the CDate function. Finally, the TypeName function is similar to VarType but returns the current data type as a readable string:

v = 123.45: Print TypeName(v)       ' Displays "Double"
Set v = Text1: Print TypeName(v)    ' Displays "TextBox"

One last point: Variant variables can also hold arrays. For more information, read the section about arrays later in this chapter.

The Decimal Data Type

Decimal is a floating-point data type with a higher precision than Double, but it has a smaller range. In fact, you can store values in the range plus or minus 79,228,162,514,264,337,593,543,950,335 with no decimal point, or plus or minus 7.9228162514264337593543950335 with 28 places to the right of the decimal point. The smallest nonzero number is plus or minus 0.0000000000000000000000000001. Decimal is a singular case among the data types supported by Visual Basic in that you can't explicitly declare a variable using As Decimal. Instead, you assign a value to a Variant variable using the CDec conversion function, for example:

Dim v As Variant
v = CDec(Text1.Text)

Once you have assigned a Decimal value to a Variant, you can perform all the usual math operations. You don't need to ensure that both operands are of Decimal type because Visual Basic will do the necessary conversions for you. Decimal is an exception among Variant subtypes in that it exploits all the bytes in the Variant structure, that is, all 14 bytes that follow the subtype identifier. If you apply the VarType function to a Variant containing a Decimal value, you get the return value of 14-vbDecimal.